#include"lambdanode.h"
#include<sstream>

ChoiceNode::ChoiceNode(ExpressionNode * c, ExpressionNode * t, ExpressionNode * f) : cond(c), iftrue(t), iffalse(f) {}
ChoiceNode::ChoiceNode(ChoiceNode const & c) : cond(c.cond -> copy()), iftrue(c.iftrue -> copy()), iffalse(c.iffalse -> copy()) {}
ChoiceNode::~ChoiceNode() {
	delete cond;
	delete iftrue;
	delete iffalse;
}
	
ExpressionNode * ChoiceNode::eval(ProgramNode const & state) const {
	ExpressionNode * choice = cond -> eval(state);
	IntegerNode c = choice -> deint();
	delete choice;
	if (c.undef)
		return new IntegerNode(0, true);
	return (c.val != 0) ? (iftrue -> eval(state)) : (iffalse -> eval(state));
}

bool ChoiceNode::isFreeVar(VariableNode const & v) const {
	return (cond -> isFreeVar(v)) || (iftrue -> isFreeVar(v)) || (iffalse -> isFreeVar(v));
}

bool ChoiceNode::isBoundVar(VariableNode const & v) const {
	return (cond -> isBoundVar(v)) || (iftrue -> isBoundVar(v)) || (iffalse -> isBoundVar(v));
}
	
ExpressionNode * ChoiceNode::subst(VariableNode const & v, ExpressionNode const * e) {
	return new ChoiceNode(cond -> subst(v, e), iftrue -> subst(v, e), iffalse -> subst(v, e));
}

ProgramNode * ChoiceNode::defun() const {
	return 0;
}

IntegerNode ChoiceNode::deint() const {
	return IntegerNode(0, true);
}
	
ExpressionNode * ChoiceNode::copy() const {
	return new ChoiceNode(*this);
}
	
string ChoiceNode::toString() const {
	stringstream s;
	s << "(" << cond -> toString() << " ? " << iftrue -> toString() << " : " << iffalse -> toString() << ")";
	return s.str();
}

ApplicationNode::ApplicationNode(ExpressionNode * l, ExpressionNode * r) : left(l), right(r) {}
	
ApplicationNode::ApplicationNode(ApplicationNode const & a) : left(a.left -> copy()), right(a.right -> copy()) {}
	
ApplicationNode::~ApplicationNode() {
	delete left;
	delete right;
}
	
ExpressionNode * ApplicationNode::eval(ProgramNode const & state) const {

	ExpressionNode * le = left -> eval(state);
	ProgramNode * df = le -> defun();
	if (df == 0) {
		delete le;
		return new IntegerNode(0, true);
	}
	else {
		ExpressionNode * e = (df -> value) -> subst(df -> name, right);
		ExpressionNode * res = e -> eval(state);
		delete le;
		delete df;
		delete e;
		return res;
	}
}
	
bool ApplicationNode::isFreeVar(VariableNode const & v) const {
	return (left -> isFreeVar(v)) || (right -> isFreeVar(v));
}

bool ApplicationNode::isBoundVar(VariableNode const & v) const {
	return (left -> isBoundVar(v)) || (right -> isBoundVar(v));
}

ExpressionNode * ApplicationNode::subst(VariableNode const & v, ExpressionNode const * e) {
	return new ApplicationNode(left -> subst(v, e), right -> subst(v, e));
}
	
ProgramNode * ApplicationNode::defun() const {
	return 0;
}

IntegerNode ApplicationNode::deint() const {
	return IntegerNode(0, true);
}
	
ExpressionNode * ApplicationNode::copy() const {
	return new ApplicationNode(*this);
}
	
string ApplicationNode::toString() const {
	stringstream s;
	s << "(" << left -> toString() << " " << right -> toString() << ")";
	return s.str();
}

FunctionNode::FunctionNode(VariableNode const & v, ExpressionNode * e) : var(v), expr(e) {}
	
FunctionNode::FunctionNode(FunctionNode const & f) : var(f.var), expr(f.expr -> copy()) {}
	
FunctionNode::~FunctionNode() {
	delete expr;
}
	
ExpressionNode * FunctionNode::eval(ProgramNode const & state) const {
	return copy();
}

bool FunctionNode::isFreeVar(VariableNode const & v) const {
	return (var == v) ? false : (expr -> isFreeVar(v));
}

bool FunctionNode::isBoundVar(VariableNode const & v) const {
	return (var == v) ? true : (expr -> isBoundVar(v));
}

ExpressionNode * FunctionNode::subst(VariableNode const & v, ExpressionNode const * e) {
	if (!(v == var)){
		if (e->isFreeVar(var)) {
        VariableNode newvar = var.rename();
        ExpressionNode * ex = expr -> subst(var, &newvar);
        ExpressionNode * exp = ex -> subst(v, e);
        delete ex;
        return new FunctionNode(newvar, exp); 
      }
		return new FunctionNode(var,expr -> subst(v, e));
	}
	else
		return copy();

}

ProgramNode * FunctionNode::defun() const {
	return new ProgramNode(var, expr);
}

IntegerNode FunctionNode::deint() const {
	return IntegerNode(0, true);
}
	
ExpressionNode * FunctionNode::copy() const {
	return new FunctionNode(*this);
}
	
string FunctionNode::toString() const {
	stringstream s;
	s << "{" << var.toString() << " -> " << expr -> toString() << "}";
	return s.str();
}
